package com.atguigu.srb.sms.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.asserts.Asserts;
import com.atguigu.srb.base.consts.SrbConsts;
import com.atguigu.srb.base.exception.BusinessException;
import com.atguigu.srb.base.response.R;
import com.atguigu.srb.base.response.ResponseEnum;
import com.atguigu.srb.common.util.RandomUtils;
import com.atguigu.srb.common.util.RegexValidateUtils;
import com.atguigu.srb.sms.config.SmsProperties;
import com.atguigu.srb.sms.feign.CoreFeignClient;
import com.atguigu.srb.sms.service.SmsService;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class SmsServiceImpl implements SmsService {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    CoreFeignClient coreFeignClient;
    @Override
    public void sendMsg(String mobile, Integer type) {
        //1、校验手机号码格式
        Asserts.assertNotTrue(!RegexValidateUtils.checkCellphone(mobile) ,
                ResponseEnum.MOBILE_ERROR);
        //2、区分获取验证码的类型 调用不同的业务方法处理
        switch (type){
            case 1:
                //注册
                sendRegistCode(mobile);
                break;
            case 2:
                //登录
                break;
            default:
                sendRegistCode(mobile);
                break;
        }

    }

    private void sendRegistCode(String mobile) {
        //TODO:重复注册校验
        //熔断降级原理就是对远程调用进行了try catch
        R r = coreFeignClient.isRegisted(mobile);
        Asserts.assertNotTrue(r.getCode()!=0,ResponseEnum.ERROR);
        boolean flag = Boolean.parseBoolean(r.getData().get("flag").toString());
        //手机号被占用 抛出异常
        Asserts.assertNotTrue(!flag,ResponseEnum.MOBILE_EXIST_ERROR);
        //发送注册验证码前，到userInfo表中校验该手机号是否已被占用
        //用户表是srb-core的表 需要访问它的接口才可以操作：需要服务间的远程访问
        //为什么不使用mq？校验的业务不能解耦合
        //发送验证码短信
        //获取验证码次数校验： 2分钟只能获取1次，
        String sendCodeLimitKey = SrbConsts.SEND_CODE_LIMIT_PREFIX +mobile;
        Asserts.assertNotTrue(stringRedisTemplate.hasKey(sendCodeLimitKey),
                ResponseEnum.ALIYUN_SMS_LIMIT_CONTROL_ERROR);
        //一天只能获取5条
        String sendCodeCountKey = SrbConsts.SEND_CODE_COUNT_PREFIX + mobile;
        String countStr = stringRedisTemplate.opsForValue().get(sendCodeCountKey);
        Asserts.assertNotTrue(countStr!=null&&(Integer.parseInt(countStr)>=5),
                ResponseEnum.ALIYUN_SMS_COUNT_CONTROL_ERROR);
        try {
            //生成验证码 调用第三方接口发送短信
            String code = RandomUtils.getFourBitRandom();
            String url = SmsProperties.URL;
            String appCode = SmsProperties.APP_CODE;
            Map<String, String> params = new HashMap<>();
            //发送短信的手机号
            params.put("receive",mobile);// "13298141807");
            params.put("tag", code);
            //短信模板id：
            params.put("templateId", SmsProperties.REGIST_TEMPLATE_ID);
            //发送post请求得到响应体结果
//            String result = postForm(appCode, url, params);
//            Map map = JSON.parseObject(result, Map.class);
//            Asserts.assertNotTrue(!"200".equals(map.get("code").toString()),
//                    ResponseEnum.ALIYUN_SMS_ERROR);
            //短信发送成功：
            // 将验证码存到redis中  并设置过期时间  15分钟
            stringRedisTemplate.opsForValue().set(SrbConsts.SEND_CODE_PREFIX+mobile,code,15,
                    TimeUnit.MINUTES);

            // 更新手机号获取验证码的次数
            //2分钟只能获取1次，一天只能获取5条
            stringRedisTemplate.opsForValue().set(sendCodeLimitKey,"1",2,
                    TimeUnit.MINUTES);
            //自增方法： 可以在之前值基础上+1,如果key不存在，默认基于0加1
            Long count = stringRedisTemplate.opsForValue().increment(sendCodeCountKey);
            if(count==1){
                //表示手机号今天第一次获取验证码，给键设置过期时间: 1天
                stringRedisTemplate.expire(sendCodeCountKey,1,TimeUnit.DAYS);
            }

        } catch (Exception e) {
            throw new BusinessException(ResponseEnum.ALIYUN_SMS_ERROR);
        }

    }


    public static String postForm(String appCode, String url, Map<String, String> params) throws IOException {
        url = url + buildRequestUrl(params);
        OkHttpClient client = new OkHttpClient.Builder().build();
        FormBody.Builder formbuilder = new FormBody.Builder();
        Iterator<String> it = params.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            formbuilder.add(key, params.get(key));
        }
        FormBody body = formbuilder.build();
        Request request = new Request.Builder().url(url).addHeader("Authorization", "APPCODE " + appCode).post(body).build();
        Response response = client.newCall(request).execute();
        System.out.println("返回状态码" + response.code() + ",message:" + response.message());
        String result = response.body().string();
        return result;
    }

    public static String buildRequestUrl(Map<String, String> params) {
        StringBuilder url = new StringBuilder("?");
        Iterator<String> it = params.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            url.append(key).append("=").append(params.get(key)).append("&");
        }
        return url.toString().substring(0, url.length() - 1);
    }
}
